//! Supervision strategies enum for handling actor failures

use std::time::Duration;
use std::sync::Arc;
use std::any::Any;

use crate::actor::{PID, SupervisorDirective, ActorError, DefaultActorContext};
use crate::supervision::{OneForOneStrategy, AllForOneStrategy, ExponentialBackoffStrategy, Supervisor};

/// An enum that encapsulates all possible supervision strategy types
#[derive(Clone)]
pub enum SupervisorStrategyEnum {
    /// One-for-one strategy (restart only the failed actor)
    OneForOne(OneForOneStrategy),
    /// All-for-one strategy (restart all siblings when one fails)
    AllForOne(AllForOneStrategy),
    /// Exponential backoff strategy (increasing delay between restarts)
    ExponentialBackoff(ExponentialBackoffStrategy),
}

impl Supervisor for SupervisorStrategyEnum {
    fn get_directive(&self, reason: &ActorError) -> SupervisorDirective {
        match self {
            SupervisorStrategyEnum::OneForOne(strategy) => strategy.get_directive(reason),
            SupervisorStrategyEnum::AllForOne(strategy) => strategy.get_directive(reason),
            SupervisorStrategyEnum::ExponentialBackoff(strategy) => strategy.get_directive(reason),
        }
    }
    
    fn handle_failure(
        &self,
        ctx: &mut DefaultActorContext,
        pid: &PID,
        reason: &ActorError,
        message: Option<Box<dyn Any + Send>>,
    ) -> SupervisorDirective {
        match self {
            SupervisorStrategyEnum::OneForOne(strategy) => strategy.handle_failure(ctx, pid, reason, message),
            SupervisorStrategyEnum::AllForOne(strategy) => strategy.handle_failure(ctx, pid, reason, message),
            SupervisorStrategyEnum::ExponentialBackoff(strategy) => strategy.handle_failure(ctx, pid, reason, message),
        }
    }
}

impl SupervisorStrategyEnum {
    /// Create a new one-for-one strategy
    pub fn one_for_one(
        max_retries: usize,
        within_time: Duration,
        decision_fn: fn(&ActorError) -> SupervisorDirective,
    ) -> Self {
        Self::OneForOne(OneForOneStrategy::new(max_retries, within_time, decision_fn))
    }
    
    /// Create a new all-for-one strategy
    pub fn all_for_one(
        max_retries: usize,
        within_time: Duration,
        decision_fn: fn(&ActorError) -> SupervisorDirective,
    ) -> Self {
        Self::AllForOne(AllForOneStrategy::new(max_retries, within_time, decision_fn))
    }
    
    /// Create a new exponential backoff strategy
    pub fn exponential_backoff(
        min_backoff: Duration,
        max_backoff: Duration,
        decision_fn: fn(&ActorError) -> SupervisorDirective,
    ) -> Self {
        Self::ExponentialBackoff(ExponentialBackoffStrategy::new(min_backoff, max_backoff, decision_fn))
    }
}

/// Create a default one-for-one strategy
pub fn default_one_for_one() -> SupervisorStrategyEnum {
    SupervisorStrategyEnum::one_for_one(
        10,
        Duration::from_secs(30),
        |_| SupervisorDirective::Restart,
    )
}

/// Create a default all-for-one strategy
pub fn default_all_for_one() -> SupervisorStrategyEnum {
    SupervisorStrategyEnum::all_for_one(
        10,
        Duration::from_secs(30),
        |_| SupervisorDirective::Restart,
    )
}

/// Create a default exponential backoff strategy
pub fn default_exponential_backoff() -> SupervisorStrategyEnum {
    SupervisorStrategyEnum::exponential_backoff(
        Duration::from_millis(500),
        Duration::from_secs(30),
        |_| SupervisorDirective::Restart,
    )
} 